home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / swagg_m.zip / MEMORY.SWG / 0055_Call Stack Reporter.pas < prev    next >
Pascal/Delphi Source File  |  1994-08-24  |  17KB  |  615 lines

  1. {---------------------------------------------------------}
  2. {  Project : Call Stack Reporter                          }
  3. {  Auteur  : Ir. G.W. van der Vegt                        }
  4. {            Hondsbroek 57                                }
  5. {            6121 XB Born                                 }
  6. {---------------------------------------------------------}
  7. {  Datum .tijd  Revisie                                   }
  8. {  920713.2100  Creatie.                                  }
  9. {  920715.2330  Trace at normal exit (exitcode=0) removed.}
  10. {  920805.2230  Path removed from filename in trace       }
  11. {  920806.2200  Blanks filled in, RunTime Library routines}
  12. {               now traced to.                            }
  13. {  921026.2000  Textmode(lastmode) added to default       }
  14. {               Csr_report. Objects & overlay tracing     }
  15. {               tested.                                   }
  16. {  921118.1400  Exitcode doesn't trigger trace anymore    }
  17. {  931114.1430  Keyboard flush in exitprocedure           }
  18. {  940201.2200  Made independed of Routines.              }
  19. {---------------------------------------------------------}
  20. {  To do        Trace Virtual Methode Table (VMT)         }
  21. {---------------------------------------------------------}
  22.  
  23. {$D+}
  24. {$L+}
  25.  
  26. {---------------------------------------------------------}
  27. {----This unit gives the line numbers & filenames at error}
  28. {    The result is a list of the call stack as produced by}
  29. {    the Turbo Pascal IDE.                                }
  30. {                                                         }
  31. {    The internal text mode report function can be        }
  32. {    replaced by another one located in your program.     }
  33. {    This could be a graphics mode or printer version. It }
  34. {    must be compiled far (so use $F+ & $F- around it.    }
  35. {    It's called once for each call level.                }
  36. {                                                         }
  37. {    This program parses the MAP file to obtain the       }
  38. {    line numbers. It searches for the MAP file in the    }
  39. {    programs startup directory as obtained by            }
  40. {    PARAMSTR(0).                                         }
  41. {---------------------------------------------------------}
  42. {    To obtain all possible info compile with the         }
  43. {    following setting :                                  }
  44. {                                                         }
  45. {    OPTIONS/LINKER/MAP FILE      = DETAILED              }
  46. {    OPTION/COMPILE/DEBUG INFO    = ON                    }
  47. {                                                         }
  48. {    The last can also be forced by the $D+ compiler      }
  49. {    directive .                                          }
  50. {                                                         }
  51. {    This version traces procedures, functions through    }
  52. {    the main program and it's (overlayed) units. It also }
  53. {    traces static methodes but not virtual methodes.     }
  54. {    When tracing static methodes a phantom entry with    }
  55. {    an call address located oon the heap is generated.   }
  56. {    The trace is stopped at the first call to a virtual  }
  57. {    methode. In a future version VMT tracing will be     }
  58. {    added as soon as I start using virtual methodes.     }
  59. {---------------------------------------------------------}
  60.  
  61. UNIT CSR_01;
  62.  
  63. INTERFACE
  64.  
  65. {---------------------------------------------------------}
  66. {----TYPES                                                }
  67. {---------------------------------------------------------}
  68.  
  69. TYPE
  70.   Csr_repfunc  = PROCEDURE(level : Word;csr : STRING);
  71.  
  72. {---------------------------------------------------------}
  73. {----VARIABLES                                            }
  74. {---------------------------------------------------------}
  75.  
  76. VAR
  77.   Csr_reporter : Csr_repfunc;
  78.  
  79. {---------------------------------------------------------}
  80. {----PROCEDURES/FUNCTIONS                                 }
  81. {---------------------------------------------------------}
  82.  
  83. PROCEDURE Csr_report(level : Word;csr : STRING);
  84.  
  85. {---------------------------------------------------------}
  86.  
  87. IMPLEMENTATION
  88.  
  89. Uses
  90.   CRT,
  91.   DOS;
  92.  
  93. VAR
  94.   ext     : extstr;
  95.   dir     : dirstr;
  96.   nam     : namestr;
  97.   mapfile : BOOLEAN;
  98.   map     : Text;
  99.   ft      : BOOLEAN;
  100.  
  101. CONST
  102.   space   = #32;
  103.  
  104. {---------------------------------------------------------}
  105. {----SUPPORT PROCEDURES & FUNCTIONS                       }
  106. {---------------------------------------------------------}
  107.  
  108. FUNCTION Istr(i,n : INTEGER;pad : CHAR) : STRING;
  109.  
  110. VAR
  111.   s : STRING;
  112.  
  113. BEGIN
  114.   Str(i:n,s);
  115.   IF (pad<>space)
  116.     THEN
  117.       WHILE (Pos(space,s)>0) DO
  118.         s[Pos(space,s)]:=pad;
  119.   Istr:=s;
  120. END; {of Istr}
  121.  
  122. {---------------------------------------------------------}
  123.  
  124. FUNCTION  Wstr(w : WORD;n : INTEGER) : STRING;
  125.  
  126. VAR
  127.   s : STRING;
  128.  
  129. BEGIN
  130.   Str(w:n,s);
  131.   Wstr:=s;
  132. END; {of Wstr}
  133.  
  134. {---------------------------------------------------------}
  135.  
  136. FUNCTION  Sstr(s : STRING;n : INTEGER) : STRING;
  137.  
  138. VAR
  139.   tmp : STRING;
  140.  
  141. BEGIN
  142.   tmp:=s;
  143.   IF n>=0
  144.     THEN WHILE (Length(tmp)<+n) DO Insert(space,tmp,1)
  145.     ELSE WHILE (Length(tmp)<-n) DO tmp:=tmp+space;
  146.   Sstr:=tmp;
  147. END; {of Sstr}
  148.  
  149. {---------------------------------------------------------}
  150.  
  151. PROCEDURE Beep;
  152.  
  153. BEGIN
  154.   Sound(500);
  155.   Delay(20);
  156.   Nosound;
  157. END; {of Beep}
  158.  
  159. {---------------------------------------------------------}
  160.  
  161. FUNCTION Word2Hex(w : Word) : STRING;
  162.  
  163. const
  164.   hexChars : array [0..$F] of Char = '0123456789ABCDEF';
  165.  
  166. begin
  167.   Word2Hex :=hexChars[Hi(w) shr 4]+hexChars[Hi(w) and $F]+
  168.              hexChars[Lo(w) shr 4]+hexChars[Lo(w) and $F];
  169. end; {of Word2Hex}
  170.  
  171. {---------------------------------------------------------}
  172.  
  173. Function Hex2Word(h : String) : word;
  174.  
  175. const
  176.   hexChars : String[16] = '0123456789ABCDEF';
  177.  
  178. var
  179.   f : word;
  180.  
  181. begin
  182.   f := 0;
  183.   while length(h) > 0 do
  184.      begin
  185.        if pos(Copy(h,1,1),HexChars) = 0
  186.          then f := 0
  187.          Else f := (f*16)+pos(H[1],Hexchars)-1;
  188.        delete(h,1,1);
  189.      end;
  190.   Hex2Word:= f;
  191. end; {of Hex2Word}
  192.  
  193. {---------------------------------------------------------}
  194.  
  195. FUNCTION Ptr2Hex(p : POINTER) : STRING;
  196.  
  197. BEGIN
  198.   IF (p=nil)
  199.     THEN Ptr2Hex := '   NIL   '
  200.     else Ptr2Hex := Word2hex(Seg(P^))+':'+Word2hex(Ofs(P^));
  201. END; {of Ptr2Hex}
  202.  
  203. {---------------------------------------------------------}
  204.  
  205. Procedure FlushKbd;
  206.  
  207. Begin
  208.   MemW[$40:$1C]:=MemW[$40:$1A];
  209. End; {of Fluskkbd}
  210.  
  211. {---------------------------------------------------------}
  212. {----STACK TRACE ROUTINES START HERE                      }
  213. {---------------------------------------------------------}
  214.  
  215. FUNCTION BPreg : WORD;
  216.  
  217. INLINE($55/$58); {Push BP, Pop AX}
  218.  
  219. {---------------------------------------------------------}
  220.  
  221. Procedure Findlineno(first,near : BOOLEAN;dep : Word;p : Pointer);
  222.  
  223. VAR
  224.   tmp     : String[80];
  225.  
  226.   line    : Integer;
  227.   adr     : String[9];
  228.   ch      : Char;
  229.  
  230.   fn      : STRING[80];
  231.   un      : STRING[80];
  232.  
  233.   errseg,
  234.   errofs  : Word;
  235.  
  236.   s,
  237.   lastun,
  238.   lastpr,
  239.   lastfn  : STRING[80];
  240.   lastnr  : Word;
  241.   call    : STRING[4];
  242.  
  243. BEGIN
  244.   IF near
  245.     THEN call:='near'
  246.     ELSE call:='far ';
  247.  
  248.   errseg:=Hex2word(Copy(Ptr2hex(p),1,4));
  249.   errofs:=Hex2word(Copy(Ptr2hex(p),6,4));
  250.  
  251.   lastnr:=0;
  252.   lastfn:='';
  253.   lastpr:='';
  254.   lastun:='';
  255.  
  256.   Assign(map,dir+nam+'.MAP');
  257.   {$I-} Reset(map); {$I+}
  258.   IF (IOResult=0)
  259.     THEN
  260.       BEGIN
  261.       {----Fist try on unit/program name}
  262.         s:='';
  263. {
  264.  00000H 00096H 00097H VALTOREN           CODE
  265.  
  266.   Address         Publics by Value
  267. }
  268.         WHILE NOT(Eof(map) OR
  269.                   (Pos('Publics by Value',s)>0) OR
  270.                   (Pos('Line numbers'   ,s)>0)) DO
  271.           BEGIN
  272.             Readln(map,s);
  273.             IF (Length(s)>=45) AND (s[7]='H')
  274.               THEN
  275.                 BEGIN
  276.                   IF (Errseg=Hex2Word(Copy(s,2,4))) {AND
  277.                      (Copy(s,42,4)='CODE')}
  278.                     THEN lastun:=Copy(s,23,18);
  279.                 END;
  280.           END;
  281.  
  282.       {----Strip Trailing Blanks}
  283.         WHILE (Length(lastun)>0) AND
  284.               (lastun[Length(lastun)]=#32) DO
  285.           Delete(lastun,Length(lastun),1);
  286.  
  287.       {----Second Try to find procedure name}
  288.         s:='';
  289. {
  290.   Address         Publics by Value
  291.  
  292.  0000:0000       @
  293.  000A:00CB       MENU_INIT
  294. }
  295.         WHILE NOT(Eof(map) OR
  296.                   (Pos('Line numbers',s)>0)) DO
  297.           BEGIN
  298.             Readln(map,s);
  299.             IF (Length(s)>=18) AND (s[6]=':')
  300.               THEN
  301.                 BEGIN
  302.                   IF (Errseg=Hex2Word(Copy(s,2,4)))
  303.                     THEN
  304.                       BEGIN
  305.                         IF (lastpr='')
  306.                           THEN lastpr:=Copy(s,18,Length(s)-17)
  307.                           ELSE
  308.                             IF (Errofs>=Hex2Word(Copy(s,7,4)))
  309.                               THEN lastpr:=Copy(s,18,Length(s)-17);
  310.                       END;
  311.                 END;
  312.           END;
  313.  
  314.       {----Strip Trailing Blanks}
  315.         WHILE (Length(lastpr)>0) AND
  316.               (lastpr[Length(lastpr)]=#32) DO
  317.           Delete(lastpr,Length(lastpr),1);
  318.  
  319.       {----Third try on line numbers & sourcefile names}
  320.         REPEAT
  321. {
  322.   Line numbers for TEST_ERROR(TEST_ERR.PAS) segment TEST_ERROR
  323. }
  324.           IF (Pos('Line numbers',s)>0)
  325.             THEN
  326.               BEGIN
  327.                 Delete(s,1,17);
  328.                 un:=Copy(s,1,Pos('(',s)-1);
  329.                 Delete(s,1,Pos('(',s));
  330.                 fn:=Copy(s,1,Pos(')',s)-1);
  331.  
  332.                 While Pos('\',fn)>0 DO Delete (fn,1,Pos('\',fn));
  333.  
  334.                 Readln(map);
  335.                 REPEAT
  336. {
  337.   15 0000:0008    16 0000:0017    18 0000:00C4    28 0000:00D2
  338. }
  339.                   Read(map,line);
  340.                   Read(map,ch);
  341.                   Read(map,adr);
  342.                   IF (Errseg=Hex2Word(Copy(adr,1,4)))
  343.                     THEN
  344.                       BEGIN
  345.                         lastfn:=fn;
  346.                         IF (Errofs>=Hex2Word(Copy(adr,6,4)))
  347.                           THEN lastnr:=line;
  348.                       END;
  349.  
  350.                   If Eoln(map)
  351.                     Then Readln(map);
  352.  
  353.                 UNTIL Eoln(map);
  354.               END;
  355.  
  356.             IF NOT(eof(map))
  357.               THEN Readln(map,s);
  358.  
  359.           UNTIL Eof(map) OR ((lastnr<>0) OR (lastfn<>''));
  360.  
  361.         Close(map);
  362.  
  363.         Beep;
  364.  
  365.         IF (lastfn<>'') AND ((errseg<>0) OR (errofs<>0))
  366.           THEN
  367.           {----Report Line Number & Source File}
  368.             BEGIN
  369.               WHILE (length(lastfn)<12) DO Insert(#32,lastfn,1);
  370.               If first
  371.                 THEN
  372.                   Csr_reporter(dep,'Runtime error '+Istr(exitcode,3,'0')+
  373.                                                   ' in line '+Wstr(lastnr,4)+
  374.                                                   ' of '+lastfn+
  375.                                                   ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.')
  376.                 ELSE
  377.                   Csr_reporter(dep,'    Called '+call+' from line '+Wstr(lastnr,4)+
  378.                                                       ' of '+lastfn+
  379.                                                       ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.');
  380.             END
  381.           ELSE
  382.             BEGIN
  383.               IF (lastun<>'') OR (lastpr<>'')
  384.                 THEN
  385.                 {----Report Unit/Program Name & Procedure name}
  386.                   BEGIN
  387.                     IF (Pos('@',lastpr)>0)
  388.                       THEN s:=lastun+'.MAIN'
  389.                       ELSE s:=lastun+'.'+lastpr;
  390.  
  391.                     WHILE (Length(s)>25) DO
  392.                       Delete(s,Length(s),1);
  393.  
  394.                     If first
  395.                       THEN
  396.                         Csr_reporter(dep,'Runtime error '+Istr(exitcode,3,'0')+
  397.                                                         ' in '+Sstr(s,25)+
  398.                                                         ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.')
  399.                       ELSE
  400.                         Csr_reporter(dep,'    Called '+call+' from '+Sstr(s,25)+
  401.                                                             ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.');
  402.                   END
  403.                 ELSE
  404.                 {----Report Error Address Only}
  405.                   BEGIN
  406.                     If first
  407.                       THEN
  408.                         Csr_reporter(dep,'Runtime error '+Istr(exitcode,3,'0')+
  409.                                                         '             '+
  410.                                                         '                '+
  411.                                                         ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.')
  412.                       ELSE
  413.                         Csr_reporter(dep,'    Called '+call+' from line     '+
  414.                                                            '                '+
  415.                                                            ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.');
  416.                   END;
  417.             END;
  418.       END
  419.     ELSE
  420.     {----Report Error Addres Only}
  421.       Csr_reporter(dep,'Runtime error '+Istr(exitcode,0,'0')+
  422.                                       ' at '+Word2hex(errseg)+':'+Word2Hex(errofs)+'.')
  423. END; {of Findlineno}
  424.  
  425. {---------------------------------------------------------}
  426. {$F+}
  427.  
  428. VAR
  429.   exitsave : POINTER;
  430.  
  431. PROCEDURE Myexit;
  432.  
  433. VAR
  434.   ch  : Char;
  435.   cdiv,
  436.   csmin,
  437.   cs,
  438.   sp,
  439.   ss  : WORD;
  440.   p   : Pointer;
  441.   dep : WORD;
  442.   j   : INTEGER;
  443.  
  444. BEGIN
  445.   Flushkbd;
  446.  
  447.   Exitproc:=exitsave;
  448.  
  449.   IF (exitcode=0) OR (erroraddr=NIL) THEN Exit;
  450.  
  451.   sp:=BPreg;
  452.   ss:=SSeg;
  453.  
  454. {----Calculate calling depth}
  455.   dep:=0;
  456.   p:=Ptr(ss,sp);
  457.   WHILE MemW[ss:Ofs(p^)]<>0 DO
  458.     BEGIN
  459.       IF (Mem[cs:MemW[ss:Ofs(p^)+2]-3]<>$E8)
  460.         THEN cs:=MemW[ss:Ofs(p^)+4];
  461.  
  462.       p:=Ptr(ss,MemW[ss:Ofs(p^)]);
  463.       Inc(dep);
  464.     END;
  465.  
  466.   p:=Ptr(ss,sp);
  467.   cdiv :=Cseg-cs;
  468.   csmin:=cs;
  469.   cs   :=Cseg;
  470.  
  471. {----Report Runtime address}
  472.   Findlineno(true,true,dep,erroraddr);
  473.   Dec(dep);
  474.  
  475. {----Calculate cseg at runtime error}
  476.   cs:=csmin+Seg(erroraddr^);
  477.  
  478. {----Prevent Turbo Pascal from reporting}
  479.   Erroraddr:=NIL;
  480.  
  481.   If NOT(mapfile) THEN Exit;
  482.  
  483. {----Skip Runtime error handler entry}
  484.   IF (MemW[ss:Ofs(p^)]<>0)
  485.     THEN p:=Ptr(ss,MemW[ss:Ofs(p^)]);
  486.  
  487. {----Report Call Stack}
  488.   WHILE MemW[ss:Ofs(p^)]<>0 DO
  489.     BEGIN
  490.     {----Test for near call instruction 3 bytes before return address}
  491.       IF (Mem[cs:MemW[ss:Ofs(p^)+2]-3]=$E8)
  492.       {----Trace a near call}
  493.         THEN Findlineno(false,true,dep,Ptr(WORD(Cs+Cdiv-Cseg),MemW[ss:Ofs(p^)+2]-3))
  494.         ELSE
  495.         {----Trace a far call}
  496.           BEGIN
  497.             Cs:=MemW[ss:Ofs(p^)+4];
  498.             Findlineno(false,false,dep,Ptr(WORD(Cs+Cdiv-Cseg),MemW[ss:Ofs(p^)+2]-3));
  499.           END;
  500.  
  501.     {----Increment stackpointer}
  502.       p:=Ptr(ss,MemW[ss:Ofs(p^)]);
  503.       Dec(dep);
  504.     END;
  505.  
  506. END; {of Myexit}
  507.  
  508. {---------------------------------------------------------}
  509.  
  510. PROCEDURE Csr_report(level : Word;csr : STRING);
  511.  
  512. BEGIN
  513.   IF ft
  514.     THEN
  515.       BEGIN
  516.         Textmode(lastmode);
  517.         ft:=false;
  518.       END;
  519.   Writeln(csr+' (',level,')');
  520. END; {of Csr_report}
  521. {$F-}
  522. {---------------------------------------------------------}
  523.  
  524. BEGIN
  525.   exitsave:=Exitproc;
  526.   exitproc:=@Myexit;
  527.   csr_reporter:=Csr_report;
  528.  
  529.   Fsplit(Paramstr(0),dir,nam,ext);
  530.   Assign(map,dir+nam+'.MAP');
  531.   {$I-} Reset(map); {$I+}
  532.   IF (IOResult=0)
  533.     THEN
  534.       BEGIN
  535.         mapfile:=true;
  536.         Close(map);
  537.       END
  538.     ELSE mapfile:=false;
  539.  
  540.   ft:=true;
  541. END.
  542.  
  543. {  STACK UNIT NEEDED FOR CRS_01}
  544.  
  545. UNIT Stack1;
  546.  
  547. INTERFACE
  548.  
  549. PROCEDURE test2(VAR i : Integer);
  550.  
  551. IMPLEMENTATION
  552.  
  553. VAR
  554.   i : INTEGER;
  555.  
  556. {---------------------------------------------------------}
  557.  
  558. PROCEDURE test2(VAR i : Integer);
  559.  
  560. PROCEDURE test4(i : INTEGER);
  561.  
  562. VAR
  563.   tmp : Integer;
  564.  
  565. BEGIN
  566.   tmp:=0;
  567.   i:=1 div tmp;
  568. END;
  569.  
  570. BEGIN
  571.   test4(i);
  572. END;
  573.  
  574. {---------------------------------------------------------}
  575.  
  576. BEGIN
  577.   i:=1;
  578. END.
  579.  
  580.  
  581. { -------------------------------   DEMO ------------------------}
  582. {---------------------------------------------------------}
  583. PROGRAM Csrtst;
  584.  
  585. USES
  586.   CRT,
  587.   Csr_01,
  588.   Stack1;
  589.  
  590. {---------------------------------------------------------}
  591.  
  592. PROCEDURE test3;
  593.  
  594. VAR
  595.   i : INTEGER;
  596.  
  597. BEGIN
  598.   test2(i);
  599. END;
  600.  
  601. {---------------------------------------------------------}
  602.  
  603. PROCEDURE test4;
  604.  
  605. BEGIN
  606.   test3
  607. END;
  608.  
  609. {---------------------------------------------------------}
  610.  
  611. BEGIN
  612.   clrscr;
  613.   test4;
  614. END.
  615.